package com.example.signdemo.services.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.core.libs.AppException;
import com.example.signdemo.entities.UserSignH;
import com.example.signdemo.mapper.UserSignHMapper;
import com.example.signdemo.services.IUserSignHService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDate;
import java.time.format.DateTimeFormatter;

@Service
@Transactional(rollbackFor = Exception.class)
public class UserSignHImpl extends ServiceImpl<UserSignHMapper, UserSignH> implements IUserSignHService {

    @Autowired
    UserSignHMapper userSignHMapper;

    @Override
    public void sign(long userId) {
        LocalDate date = LocalDate.now();
        String id = String.format("%s-%s_%s", date.getYear(), date.getMonthValue(), userId);
        long sign = 1 << (date.getDayOfMonth() - 1);

        UserSignH userSignH = userSignHMapper.selectById(id);
        if (userSignH == null) {
            userSignH = new UserSignH();
            userSignH.setId(id);
            userSignH.setUserId(userId);
            userSignH.setSignMonth(date.getMonthValue());
        }

        if (checkIsSinged(userSignH.getSignRecord(), sign)) {
            throw new AppException("该时间已经签到");
        }

        userSignH.setSignRecord(userSignH.getSignRecord() + sign);
        userSignH.setLastSignDate(date);

        this.updateSingCount(userSignH, date);
        this.save(userSignH);
    }

    @Override
    public void againSign(long userId, String day) {
        LocalDate date;
        try {
            date = LocalDate.parse(buildYmd(LocalDate.now(), day), DateTimeFormatter.ISO_LOCAL_DATE);
        } catch (Exception e) {
            e.printStackTrace();
            throw new AppException("时间转换错误");
        }

        if (date.getDayOfMonth() >= LocalDate.now().getDayOfMonth()) {
            throw new AppException("补签日期错误");
        }

        String id = buildId(userId, date);
        long sign = 1 << (date.getDayOfMonth() - 1);

        UserSignH userSignH = userSignHMapper.selectById(id);
        if (userSignH == null) {
            userSignH = new UserSignH();
            userSignH.setId(id);
            userSignH.setUserId(userId);
            userSignH.setSignMonth(date.getMonthValue());

            this.save(userSignH);
        }

        if (checkIsSinged(userSignH.getSignRecord(), sign)) {
            throw new AppException("该时间已经签到");
        }

        userSignH.setSignRecord(userSignH.getSignRecord() + sign);
        userSignH.setLastSignDate(date);
        userSignH.setType(UserSignH.AGAIN_SIGN);

        this.updateSingCount(userSignH, date);
        this.updateById(userSignH);
    }

    /**
     * 根据ID获取当月签到次数
     *
     * @param userId
     * @return
     */
    @Override
    public int signDays(long userId) {
        UserSignH userSignH = userSignHMapper.selectById(buildId(userId, LocalDate.now()));
        if (userSignH == null) {
            throw new AppException("无效ID");
        }
        return Long.bitCount(userSignH.getSignRecord());
    }

    /**
     * 构建ID
     *
     * @param userId
     * @param date
     * @return
     */
    private String buildId(long userId, LocalDate date) {
        return String.format("%s-%s_%s", date.getYear(), date.getMonthValue(), userId);
    }

    /**
     * 构建指定日的日期
     *
     * @param date
     * @param day
     * @return
     */
    private String buildYmd(LocalDate date, String day) {
        return String.format("%s-%s-%s", date.getYear(), date.getMonthValue(), day);
    }

    /**
     * 检查是否已经签到
     *
     * @param signRecord
     * @param sign
     * @return
     */
    private boolean checkIsSinged(long signRecord, long sign) {
        if (signRecord != 0 && (signRecord & sign) > 0) {
            return true;
        }
        return false;
    }

    // 更新连续签到逻辑
    private void updateSingCount(UserSignH userSignH, LocalDate date) {
        long binary = userSignH.getSignRecord() | 1 << (date.getDayOfMonth() - 1);
        String b = Long.toBinaryString(binary);

        int count = 0;
        for (int i = 0; i < b.length(); i++) {
            if (b.charAt(i) == '1') {
                ++count;
            } else {
                break;
            }
        }
        userSignH.setSignCount(count);
    }
}
